using Microsoft.VisualStudio.TestTools.UnitTesting;

using Weazel.Gezel.Parser;
using Weazel.Gezel.Model;

namespace Weazel.Gezel.Parser.Test
{
  [TestClass]
  public class TestTokenizer
  {
    public TestTokenizer()
    {
    }

    [TestMethod]
    public void TestGetErrorInformation() {
        Tokenizer t = new Tokenizer("+ \n + ");
        
        Assert.AreEqual(Token.Add, t.CurrentToken);

        Position pos = t.CurrentPosition;

        Assert.AreEqual(pos.Line, "+ ");
        Assert.AreEqual(pos.LineNumber, 1u);
        Assert.AreEqual(pos.ColumnNumber, 1u);

        t = new Tokenizer("+ \n +\n !");
        
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Add, t.CurrentToken);

        pos = t.CurrentPosition;
        Assert.AreEqual(pos.Line, " +");
        Assert.AreEqual(pos.LineNumber, 2u);
        Assert.AreEqual(pos.ColumnNumber, 2u);

        // this time we are on the last line
        t = new Tokenizer("+ \n + ");
       
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Add, t.CurrentToken);

        pos = t.CurrentPosition;
        Assert.AreEqual(pos.Line, " + ");
        Assert.AreEqual(pos.LineNumber, 2u);
        Assert.AreEqual(pos.ColumnNumber, 2u);
      }

    /*
    [TestMethod]
    public void TestPushToken()  {
        Tokenizer t = new Tokenizer("+");
        t.NextToken();
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        t.PushToken(Token.Add);

        t.PopToken(Token.Add);
        t.PopToken(Token.Eof);

        t.PushToken(Token.Add);
        try {
            t.PushToken(Token.Add);
            Assert.Fail("exception expected");
        } catch {

        }
    }
    */

    [TestMethod]
    public void TestPopToken()  {
        Tokenizer t = new Tokenizer("(test + test)");
      
        Assert.AreEqual(Token.LeftParenthesis, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.RightParenthesis, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);      
    }

    [TestMethod]
    public void TestUnclosedLitteral()
    {
      Tokenizer t = new Tokenizer("dp mydp { \" }");
      ParseAddExpression.Parse(new Set<Token>(Token.Eof), t);
			
			bool ok = false;
			foreach (Model.Errors.ParseError error in t.Errors)
				if (error.ErrorType == Weazel.Gezel.Model.Errors.ParseErrorType.NewlineInString)
					ok = true;

			Assert.IsTrue(ok);
    }

    [TestMethod]
    public void TestEOF() {
        Tokenizer t = new Tokenizer("");        
        Assert.AreEqual(Token.Eof, t.CurrentToken);
      }

    [TestMethod]
    public void TestWhitespace() {
        Tokenizer t = new Tokenizer(" \n\t\f");        
        Assert.AreEqual(Token.Eof, t.CurrentToken);
      }

    [TestMethod]
    public void TestCppComment() {
        // simplest case
        Tokenizer t = new Tokenizer("//");              
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        // leftChild more complicated case
        t = new Tokenizer("+ \n // comment line\n +");
        
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();               
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);
      }

    [TestMethod]
    public void TestShellComment()  {
        // simplest case
        Tokenizer t = new Tokenizer("#!");           
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        // leftChild more complicated case
        t = new Tokenizer("+ \n #! comment line\n +");
     
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);
      }

    [TestMethod]
    public void TestArithmeticOperators()  {
        Tokenizer t = new Tokenizer("+");

        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        t = new Tokenizer("-");
    
        Assert.AreEqual(Token.Sub, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        t = new Tokenizer("*");
  
        Assert.AreEqual(Token.Mul, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        t = new Tokenizer("/");
 
        Assert.AreEqual(Token.Div, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        t = new Tokenizer("%");

        Assert.AreEqual(Token.Mod, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);
    }

    [TestMethod]
    public void TestLogicOperators()  {
        Tokenizer t = new Tokenizer("|&^~ | & ^ ~ ");

        Assert.AreEqual(Token.Ior, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.And, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Xor, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Not, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Ior, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.And, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Xor, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Not, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);
    }

    [TestMethod]
    public void TestMiscellaneous()  {
        Tokenizer t = new Tokenizer("[({test,@test->test;})]");

        Assert.AreEqual(Token.LeftBracket, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.LeftParenthesis, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.LeftBrace, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Comma, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.At, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Transition, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Semicolon, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.RightBrace, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.RightParenthesis, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.RightBracket, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);      
    }

    [TestMethod]
    public void TestIdentifier()  {
        string testIdentifier = "test_identifier123";
        Tokenizer t = new Tokenizer(" " + testIdentifier + " ");
  
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        Assert.AreEqual(t.CurrentString, testIdentifier);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);       
    }

    [TestMethod]
    public void TestStrings()  {
        string testStr = "Hello, World!";
        Tokenizer t = new Tokenizer(" identifierRef \"" + testStr + "\"+ ");
       
        Assert.AreEqual(Token.Identifier, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Str, t.CurrentToken);
        Assert.AreEqual(t.CurrentString, testStr);

        t.NextToken();
        Assert.AreEqual(Token.Add, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);

        try {
          // test that unclosed string throws exception
          t = new Tokenizer("\"" + testStr);          
          Assert.Fail("expected exception");
        }  catch {
        }
    }

    [TestMethod]
    public void TestNumbers() {
        Tokenizer t = new Tokenizer("2 002 0b01 0B01 0xff 0XFF");

        Assert.AreEqual(Token.Number, t.CurrentToken);
        Assert.AreEqual(t.CurrentInt, 2);
        t.NextToken();
        Assert.AreEqual(Token.Number, t.CurrentToken);
        Assert.AreEqual(t.CurrentInt, 2);
        t.NextToken();
        Assert.AreEqual(Token.Number, t.CurrentToken);
        Assert.AreEqual(t.CurrentInt, 1);
        t.NextToken();
        Assert.AreEqual(Token.Number, t.CurrentToken);
        Assert.AreEqual(t.CurrentInt, 1);
        t.NextToken();
        Assert.AreEqual(Token.Number, t.CurrentToken);
        Assert.AreEqual(t.CurrentInt, 255);
        t.NextToken();
        Assert.AreEqual(Token.Number, t.CurrentToken);
        Assert.AreEqual(t.CurrentInt, 255);
        t.NextToken();
        Assert.AreEqual(Token.Eof, t.CurrentToken);
    }

    [TestMethod]
    public void TestComparisonsAndShifts()  {
        Tokenizer t = new Tokenizer("== = != < > <= >= << >> ! =");
  
        Assert.AreEqual(Token.Eq, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Assign, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Neq, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Lt, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Gt, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Lte, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Gte, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Sla, t.CurrentToken);
        t.NextToken();
        Assert.AreEqual(Token.Sra, t.CurrentToken);

        try {
            t.NextToken();
            Assert.Fail("exception expected");
          } catch {
        }
        t.NextToken();
        Assert.AreEqual(Token.Assign, t.CurrentToken);
    }    
  }
}
